home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 2002 November / SGI Freeware 2002 November - Disc 2.iso / dist / fw_glimpse.idb / usr / freeware / src / glimpse-3.0 / libtemplate / template / template.c.z / template.c
C/C++ Source or Header  |  1997-09-09  |  19KB  |  816 lines

  1. static char rcsid[] = "$Id: template.c,v 1.17 1995/02/01 22:12:59 hardy Exp $";
  2. /*
  3.  *  template.c - SOIF Object ("template") processing code for Harvest 
  4.  *
  5.  *  Darren Hardy, hardy@cs.colorado.edu, February 1994
  6.  *
  7.  *  ----------------------------------------------------------------------
  8.  *  Copyright (c) 1994, 1995.  All rights reserved.
  9.  *  
  10.  *          Mic Bowman of Transarc Corporation.
  11.  *          Peter Danzig of the University of Southern California.
  12.  *          Darren R. Hardy of the University of Colorado at Boulder.
  13.  *          Udi Manber of the University of Arizona.
  14.  *          Michael F. Schwartz of the University of Colorado at Boulder. 
  15.  *  
  16.  *  This copyright notice applies to all code in Harvest other than
  17.  *  subsystems developed elsewhere, which contain other copyright notices
  18.  *  in their source text.
  19.  *  
  20.  *  The Harvest software was developed by the Internet Research Task
  21.  *  Force Research Group on Resource Discovery (IRTF-RD).  The Harvest
  22.  *  software may be used for academic, research, government, and internal
  23.  *  business purposes without charge.  If you wish to sell or distribute
  24.  *  the Harvest software to commercial clients or partners, you must
  25.  *  license the software.  See
  26.  *  http://harvest.cs.colorado.edu/harvest/copyright,licensing.html#licensing.
  27.  *  
  28.  *  The Harvest software is provided ``as is'', without express or
  29.  *  implied warranty, and with no support nor obligation to assist in its
  30.  *  use, correction, modification or enhancement.  We assume no liability
  31.  *  with respect to the infringement of copyrights, trade secrets, or any
  32.  *  patents, and are not responsible for consequential damages.  Proper
  33.  *  use of the Harvest software is entirely the responsibility of the user.
  34.  *  
  35.  *  For those who are using Harvest for non-commercial purposes, you may
  36.  *  make derivative works, subject to the following constraints:
  37.  *  
  38.  *  - You must include the above copyright notice and these accompanying 
  39.  *    paragraphs in all forms of derivative works, and any documentation 
  40.  *    and other materials related to such distribution and use acknowledge 
  41.  *    that the software was developed at the above institutions.
  42.  *  
  43.  *  - You must notify IRTF-RD regarding your distribution of the 
  44.  *    derivative work.
  45.  *  
  46.  *  - You must clearly notify users that your are distributing a modified 
  47.  *    version and not the original Harvest software.
  48.  *  
  49.  *  - Any derivative product is also subject to the restrictions of the 
  50.  *    copyright, including distribution and use limitations.
  51.  */
  52. #include <stdio.h>
  53. #include <string.h>
  54. #include <stdlib.h>
  55. #include <ctype.h>
  56. #include "util.h"
  57. #include "template.h"
  58.  
  59. /* Local functions */
  60. static void output_char();
  61. static void output_buffer();
  62. static int attribute_cmp();
  63.  
  64. /*
  65.  *  create_AVList() - Creates an Attribute-Value node to include in an AVList
  66.  */
  67. AVList *create_AVList(attr, value, vsize)
  68. char *attr;
  69. char *value;
  70. int vsize;
  71. {
  72.     static AVList *l;
  73.  
  74.     l = xmalloc(sizeof(AVList));
  75.     l->data = xmalloc(sizeof(AVPair));
  76.     l->data->value = xmalloc(vsize + 1);
  77.  
  78.     l->data->attribute = strdup(attr);
  79.     l->data->vsize = vsize;
  80.     memcpy(l->data->value, value, l->data->vsize);
  81.     l->data->value[l->data->vsize] = '\0';
  82.     l->data->offset = -1;
  83.     l->next = NULL;
  84.  
  85.     return (l);
  86. }
  87.  
  88. /*
  89.  *  free_AVList() - Cleans up an AVList
  90.  */
  91. void free_AVList(list)
  92. AVList *list;
  93. {
  94.     AVList *walker = list, *t;
  95.  
  96.     while (walker) {
  97.         if (walker->data)
  98.             free_AVPair(walker->data);
  99.         t = walker;
  100.         walker = walker->next;
  101.         xfree(t);
  102.     }
  103. }
  104.  
  105. /*
  106.  *  free_AVPair() - Cleans up an AVPair
  107.  */
  108. void free_AVPair(avp)
  109. AVPair *avp;
  110. {
  111.     if (!avp)
  112.         return;
  113.     if (avp->attribute)
  114.         xfree(avp->attribute);
  115.     if (avp->value)
  116.         xfree(avp->value);
  117.     xfree(avp);
  118. }
  119.  
  120. /*
  121.  *  add_offset() - Adds the offset value to the AVPair matching attribute.
  122.  */
  123. void add_offset(l, attr, off)
  124. AVList *l;
  125. char *attr;
  126. size_t off;
  127. {
  128.     AVPair *avp = extract_AVPair(l, attr);
  129.  
  130.     if (avp != NULL)
  131.         avp->offset = off;
  132. }
  133.  
  134. /*
  135.  *  extract_AVPair() - Searches for the given attribute in the AVList.
  136.  *  Does a case insensitive match on the attributes.  Returns NULL
  137.  *  on error; otherwise returns the matching AVPair.
  138.  */
  139. AVPair *extract_AVPair(list, attribute)
  140. AVList *list;
  141. char *attribute;
  142. {
  143.     AVList *walker;
  144.  
  145.     for (walker = list; walker; walker = walker->next) {
  146.         if (!strcasecmp(walker->data->attribute, attribute))
  147.             return (walker->data);
  148.     }
  149.     return (NULL);
  150. }
  151.  
  152. /*
  153.  *  exists_AVList() - Checks to see if an AVPair exists for the given
  154.  *  attribute.  Returns non-zero if it does; 0 if it doesn't.
  155.  */
  156. int exists_AVList(list, attr)
  157. AVList *list;
  158. char *attr;
  159. {
  160.     return (extract_AVPair(list, attr) != NULL ? 1 : 0);
  161. }
  162.  
  163.  
  164. /*
  165.  *  add_AVList() - Adds the Attribute-Value pair to the given AVList
  166.  */
  167. void add_AVList(list, attr, value, vsize)
  168. AVList *list;
  169. char *attr;
  170. char *value;
  171. int vsize;
  172. {
  173.     AVList *walker = list;
  174.  
  175.     if (list == NULL)
  176.         return;
  177.  
  178.     /* move to the end of the list, and add a node */
  179.     while (walker->next) {
  180.         /* Don't add a duplicate Attribute, just replace it */
  181.         if (!strcasecmp(attr, walker->data->attribute)) {
  182.             xfree(walker->data->value);
  183.             walker->data->vsize = vsize;
  184.             walker->data->value = xmalloc(vsize + 1);
  185.             memcpy(walker->data->value, value, vsize);
  186.             walker->data->value[vsize] = '\0';
  187.             return;
  188.         }
  189.         walker = walker->next;
  190.     }
  191.     walker->next = create_AVList(attr, value, vsize);
  192. }
  193.  
  194. /*
  195.  *  FAST_add_AVList() - Quick version of add_AVList().  Doesn't check
  196.  *  for duplicates.  attr MUST be unique to the list.
  197.  */
  198. void FAST_add_AVList(list, attr, value, vsize)
  199. AVList *list;
  200. char *attr;
  201. char *value;
  202. int vsize;
  203. {
  204.     AVList *t;
  205.  
  206.     if (list == NULL)
  207.         return;
  208.  
  209.     t = create_AVList(attr, value, vsize);
  210.     t->next = list->next;
  211.     list->next = t;
  212. }
  213.  
  214.  
  215. /*
  216.  *  merge_AVList() - Merges the b list into the a list.  If the AVPair
  217.  *  in b exists in the a list, then the data is replaced.  Otherwise,
  218.  *  the data is appended to the list.
  219.  */
  220. AVList *merge_AVList(a, b)
  221. AVList *a, *b;
  222. {
  223.     AVList *walker = b;
  224.     AVPair *avp;
  225.  
  226.     if (a == NULL)
  227.         return (NULL);
  228.  
  229.     while (walker) {
  230.         avp = extract_AVPair(a, walker->data->attribute);
  231.         if (avp != NULL) {
  232.             /* replace the data */
  233.             xfree(avp->value);
  234.             avp->value = xmalloc(walker->data->vsize);
  235.             memcpy(avp->value, walker->data->value,
  236.                    walker->data->vsize);
  237.             avp->vsize = walker->data->vsize;
  238.             avp->offset = walker->data->offset;
  239.         } else {
  240.             /* append it to 'a' */
  241.             add_AVList(a, walker->data->attribute,
  242.                    walker->data->value, walker->data->vsize);
  243.             add_offset(a, walker->data->attribute,
  244.                    walker->data->offset);
  245.         }
  246.         walker = walker->next;
  247.     }
  248.     return (a);
  249. }
  250.  
  251. /*
  252.  *  append_AVList() - Adds the Attribute-Value pair to the given AVList.
  253.  *  If the attr is present in the list, then it appends the value to
  254.  *  the previous value.
  255.  */
  256. void append_AVList(list, attr, value, vsize)
  257. AVList *list;
  258. char *attr;
  259. char *value;
  260. int vsize;
  261. {
  262.     AVPair *avp;
  263.     char *buf;
  264.  
  265.     if ((avp = extract_AVPair(list, attr)) == NULL) {
  266.         add_AVList(list, attr, value, vsize);
  267.     } else {        /* replace the data */
  268.         buf = xmalloc(avp->vsize + vsize + 2);
  269.         memcpy(buf, avp->value, avp->vsize);
  270.         buf[avp->vsize] = '\n';
  271.         memcpy(buf + avp->vsize + 1, value, vsize);
  272.         xfree(avp->value);
  273.         avp->value = buf;
  274.         avp->vsize += vsize + 1;
  275.         avp->offset = -1;
  276.     }
  277. }
  278.  
  279. /*
  280.  *  create_template() - Creats a new Template structure.
  281.  */
  282. Template *create_template(type, url)
  283. char *type;
  284. char *url;
  285. {
  286.     static Template *t = NULL;
  287.  
  288.     t = xmalloc(sizeof(Template));
  289.     if (type == NULL)
  290.         t->template_type = strdup("FILE");
  291.     else
  292.         t->template_type = strdup(type);
  293.     t->url = strdup(url);
  294.     t->list = NULL;
  295.     t->offset = -1;
  296.     t->length = -1;
  297.     return (t);
  298. }
  299.  
  300. /*
  301.  *  free_template() - Cleans up the template.
  302.  */
  303. void free_template(t)
  304. Template *t;
  305. {
  306.     if (!t)
  307.         return;
  308.     if (t->list)
  309.         free_AVList(t->list);
  310.     if (t->template_type)
  311.         xfree(t->template_type);
  312.     if (t->url)
  313.         xfree(t->url);
  314.     xfree(t);
  315. }
  316.  
  317. /*
  318.  *  Template Parsing and Printing code
  319.  *
  320.  *  Template Parsing can read from memory or from a file.
  321.  *  Template Printing can write to memory or to a file.
  322.  */
  323.  
  324. static FILE *outputfile = NULL;    /* user's file */
  325. Buffer *bout = NULL;
  326.  
  327. /*
  328.  *  init_print_template() - Print template to memory buffer or to
  329.  *  a file if fp is not NULL.  Returns NULL if printing to a file;
  330.  *  otherwise returns a pointer to the Buffer where the data is stored.
  331.  */
  332. Buffer *init_print_template(fp)
  333. FILE *fp;
  334. {
  335.     if (fp) {
  336.         outputfile = fp;
  337.         return (NULL);
  338.     } else {
  339.         bout = create_buffer(BUFSIZ);
  340.         return (bout);
  341.     }
  342. }
  343.  
  344. /*
  345.  *  output_char() - writes a single character to memory or a file.
  346.  */
  347. static void output_char(c)
  348. char c;
  349. {
  350.     output_buffer(&c, 1);
  351. }
  352.  
  353. /*
  354.  *  output_buffer() - writes a buffer to memory or a file.
  355.  */
  356. static void output_buffer(s, sz)
  357. char *s;
  358. int sz;
  359. {
  360.     if (outputfile)
  361.         fwrite(s, sizeof(char), sz, outputfile);
  362.     else
  363.         add_buffer(bout, s, sz);
  364. }
  365.  
  366. /*
  367.  *  print_template() - Prints a SOIF Template structure into a file
  368.  *  or into memory.   MUST call init_print_template_file() or 
  369.  *  init_print_template_string() before, and finish_print_template() after.  
  370.  */
  371. void print_template(template)
  372. Template *template;
  373. {
  374.     /* Estimate the buffer size to prevent too many realloc() calls */
  375.     if (outputfile == NULL) {
  376.         AVList *walker;
  377.         int n = 0;
  378.  
  379.         for (walker = template->list; walker; walker = walker->next)
  380.             n += walker->data->vsize;
  381.         if (bout->length + n > bout->size)
  382.             increase_buffer(bout, n);    /* need more */
  383.     }
  384.     print_template_header(template);
  385.     print_template_body(template);
  386.     print_template_trailer(template);
  387. }
  388.  
  389. void print_template_header(template)
  390. Template *template;
  391. {
  392.     char buf[BUFSIZ];
  393.  
  394.     sprintf(buf, "@%s { %s\n", template->template_type, template->url);
  395.     output_buffer(buf, strlen(buf));
  396. }
  397.  
  398. void print_template_body(template)
  399. Template *template;
  400. {
  401.     char buf[BUFSIZ];
  402.     AVList *walker;
  403.  
  404.     for (walker = template->list; walker; walker = walker->next) {
  405.         if (walker->data->vsize == 0)
  406.             continue;
  407.         /* Write out an Attribute value pair */
  408.         sprintf(buf, "%s{%u}:\t", walker->data->attribute,
  409.             (unsigned int) walker->data->vsize);
  410.         output_buffer(buf, strlen(buf));
  411.         output_buffer(walker->data->value, walker->data->vsize);
  412.         output_char('\n');
  413.     }
  414. }
  415.  
  416. void print_template_trailer(template)
  417. Template *template;
  418. {
  419.     output_char('}');
  420.     output_char('\n');
  421.     if (outputfile != NULL)
  422.         fflush(outputfile);
  423. }
  424.  
  425. /*
  426.  *  finish_print_template() - Cleanup after printing template.
  427.  *  Buffer is no longer valid.
  428.  */
  429. void finish_print_template()
  430. {
  431.     outputfile = NULL;
  432.     if (bout)
  433.         free_buffer(bout);
  434.     bout = NULL;
  435. }
  436.  
  437.  
  438. /* Parsing templates */
  439.  
  440. static char *inputbuf = NULL;
  441. static FILE *inputfile = NULL;
  442. static int inputbufsz = 0, curp = 0;
  443. static size_t inputoffset = 0, inputlength = 0;
  444.  
  445. void init_parse_template_file(fp)
  446. FILE *fp;
  447. {
  448.     inputfile = fp;
  449.     inputoffset = ftell(fp);
  450.     inputlength = 0;
  451. }
  452.  
  453. void init_parse_template_string(s, sz)
  454. char *s;
  455. int sz;
  456. {
  457.     inputbuf = s;
  458.     inputbufsz = sz;
  459.     curp = 0;
  460.     inputfile = NULL;
  461.     inputoffset = 0;
  462.     inputlength = 0;
  463. }
  464.  
  465. void finish_parse_template()
  466. {
  467.     inputfile = NULL;
  468.     curp = 0;
  469.     inputbufsz = 0;
  470. }
  471.  
  472. int is_parse_end_of_input()
  473. {
  474.     if (inputfile != NULL)
  475.         return (feof(inputfile));
  476.     return (curp >= inputbufsz || inputbuf[curp] == '\0');
  477. }
  478.  
  479. /* input_char() -> performs c = input_char(); */
  480. #define input_char() \
  481.     if (inputfile != NULL) { \
  482.         inputoffset++; \
  483.         inputlength++; \
  484.         c = fgetc(inputfile);  \
  485.     } else if (curp >= inputbufsz || inputbuf[curp] == '\0') { \
  486.         c = (char) EOF; \
  487.     } else { \
  488.         inputoffset++; \
  489.         inputlength++; \
  490.         c = inputbuf[curp++];  \
  491.     }
  492.  
  493. static void backup_char(x)
  494. char x;
  495. {
  496.     inputoffset--;
  497.     inputlength--;
  498.     if (inputfile != NULL)
  499.         ungetc(x, inputfile);
  500.     else
  501.         curp--;
  502.     return;
  503. }
  504.  
  505.  
  506. #define skip_whitespace()    \
  507.     while (1) { \
  508.         input_char(); \
  509.         if (c == EOF) return(NULL); \
  510.         if (!isspace(c)) { backup_char(c); break; } \
  511.     }
  512.  
  513. #define skip_tab()    \
  514.     while (1) { \
  515.         input_char(); \
  516.         if (c == EOF) return(NULL); \
  517.         if (c != '\t') { backup_char(c); break; } \
  518.     }
  519.  
  520. #define skip_whitespace_and(a)    \
  521.     while (1) { \
  522.         input_char(); \
  523.         if (c == EOF) return(NULL); \
  524.         if (c == a) continue; \
  525.         if (!isspace(c)) { backup_char(c); break; } \
  526.     }
  527.  
  528. #define skip_whitespace_break()    \
  529.     while (1) { \
  530.         input_char(); \
  531.         if (c == EOF) { done = 1; break; }\
  532.         if (c == '}') { done = 1; break; }\
  533.         if (!isspace(c)) { backup_char(c); break; } \
  534.     }
  535.  
  536. #define grab_token() \
  537.     p = &buf[0]; \
  538.     while (1) { \
  539.         input_char(); \
  540.         if (c == (char) EOF) return(NULL); \
  541.         if (isspace(c)) { backup_char(c); break; } \
  542.         *p++ = c; \
  543.         if (p == &buf[BUFSIZ-1]) return(NULL); \
  544.     } \
  545.     *p = '\0';
  546.  
  547. #define grab_attribute() \
  548.     p = buf; \
  549.     while (1) { \
  550.         input_char(); \
  551.         if (c == EOF) return(NULL); \
  552.         if (c == '{') break; \
  553.         if (c == '}') break; \
  554.         *p++ = c; \
  555.         if (p == &buf[BUFSIZ-1]) return(NULL); \
  556.     } \
  557.     *p = '\0';
  558.  
  559.  
  560. /*
  561.  *  parse_template() - Returns a Template structure for the template
  562.  *  stored in memory or in a file.  MUST call init_parse_template_file()
  563.  *  or init_parse_template_string() before, and finish_parse_template()
  564.  *  after.  Returns NULL on error.
  565.  */
  566. Template *parse_template()
  567. {
  568.     static Template *template = NULL;
  569.     char buf[BUFSIZ], *p, *attribute, *value;
  570.     int vsize, i, done = 0, c;
  571.     size_t voffset;
  572.  
  573.     template = xmalloc(sizeof(Template));
  574.     while (1) {        /* Find starting point: @ */
  575.         input_char();
  576.         if (c == EOF) {
  577.             xfree(template);
  578.             return (NULL);
  579.         }
  580.         if (c == '@')
  581.             break;
  582.     }
  583.     template->offset = inputoffset;
  584.     /* Get Template-Type */
  585.     grab_token();
  586.     template->template_type = strdup(buf);
  587.  
  588.     /* Get URL */
  589.     skip_whitespace_and('{');
  590.     grab_token();
  591.     template->url = strdup(buf);
  592.     template->list = NULL;
  593.  
  594. #ifdef DEBUG
  595.     log("Grabbing Template Object: %s %s\n", template->template_type,
  596.         template->url);
  597. #endif
  598.  
  599.     while (1) {
  600.         /* Get Attribute name and value */
  601.         skip_whitespace_break();
  602.         if (done == 1)
  603.             break;
  604.         grab_attribute();
  605.         attribute = strdup(buf);
  606. #ifdef DEBUG
  607.         log("Grabbed Attribute: %s\n", attribute);
  608. #endif
  609.         grab_attribute();
  610.         vsize = atoi(buf);
  611.  
  612.         /* Get Value */
  613.         input_char();
  614.         if (c != ':') {
  615.             free_template(template);
  616.             xfree(attribute);
  617.             return (NULL);
  618.         }
  619.         input_char();
  620.         if (c != '\t') {
  621.             free_template(template);
  622.             xfree(attribute);
  623.             return (NULL);
  624.         }
  625.         /* This is a very tight loop, so optimize */
  626.         value = xmalloc(vsize + 1);
  627.         voffset = inputoffset;
  628.         if (inputfile == NULL) {    /* normal one-by-one */
  629.             for (i = 0; i < vsize; i++) {
  630.                 input_char();
  631.                 value[i] = c;
  632.             }
  633.             value[i] = '\0';
  634.         } else {            /* do the fast file copy */
  635.             if (fread(value, 1, vsize, inputfile) != vsize) {    
  636.                 free_template(template);
  637.                 xfree(attribute);
  638.                 xfree(value);
  639.                 return(NULL);
  640.             }
  641.             inputoffset += vsize;
  642.             inputlength += vsize;
  643.         }
  644.         if (template->list == NULL) {
  645.             template->list = create_AVList(attribute, value, vsize);
  646.         } else
  647.             FAST_add_AVList(template->list,attribute,value,vsize);
  648.         add_offset(template->list, attribute, voffset);
  649.         xfree(attribute);
  650.         xfree(value);
  651.     }
  652.     template->length = inputlength;
  653.     return (template);
  654. }
  655.  
  656.  
  657. /* Sorting Attribute-Value Lists */
  658.  
  659. /*
  660.  *  attribute_cmp() - strcmp(3) for attributes.  Works with "Embed<n>" 
  661.  *  attributes so that they are first sorted by number, then by attribute.
  662.  *  Does case insenstive compares.
  663.  */
  664. static int attribute_cmp(a, b)
  665. char *a, *b;
  666. {
  667.     if ((tolower(a[0]) == 'e') && (tolower(b[0]) == 'e') &&        /* quickie */
  668.         !strncasecmp(a, "embed", 5) && !strncasecmp(b, "embed", 5)) {
  669.         char *p, *q;
  670.         int an, bn;
  671.  
  672.         p = strchr(a, '<');    /* Find embedded number */
  673.         q = strchr(a, '>');
  674.         if (!p || !q)
  675.             return (strcasecmp(a, b));    /* bail */
  676.         *q = '\0';
  677.         an = atoi(p + 1);
  678.         *q = '>';
  679.  
  680.         p = strchr(b, '<');    /* Find embedded number */
  681.         q = strchr(b, '>');
  682.         if (!p || !q)
  683.             return (strcasecmp(a, b));    /* bail */
  684.         *q = '\0';
  685.         bn = atoi(p + 1);
  686.         *q = '>';
  687.         if (an != bn)    /* If numbers are different */
  688.             return (an < bn ? -1 : 1);
  689.         /* otherwise, do strcmp on attr */
  690.         return (strcasecmp(strchr(a, '>') + 1, strchr(b, '>') + 1));
  691.     }
  692.     return (strcasecmp(a, b));
  693. }
  694.  
  695. /*
  696.  *  sort_AVList() - Uses an insertion sort to sort the AVList by attribute.
  697.  *  Returns the new head of the list.  
  698.  */
  699. AVList *sort_AVList(avl)
  700. AVList *avl;
  701. {
  702.     AVList *walker, *n, *a, *t;
  703.     static AVList *head;
  704.     int (*acmp) ();
  705.  
  706.     acmp = attribute_cmp;
  707.  
  708.     /* Set the first node to be the head of the new list */
  709.     head = avl;
  710.     walker = avl->next;
  711.     head->next = NULL;
  712.  
  713.     while (walker) {
  714.         /* Pick off this node */
  715.         n = walker;
  716.         walker = walker->next;
  717.         n->next = NULL;
  718.  
  719.         /* Find insertion point */
  720.         for (a = head; a->next &&
  721.           acmp(a->next->data->attribute, n->data->attribute) < 0;
  722.              a = a->next);
  723.         if (a == head) {    /* prepend to list */
  724.             if (acmp(a->data->attribute, n->data->attribute) < 0) {
  725.                 /* As the second node */
  726.                 t = a->next;
  727.                 a->next = n;
  728.                 n->next = t;
  729.             } else {
  730.                 /* As the first node */
  731.                 head = n;
  732.                 n->next = a;
  733.             }
  734.         } else {    /* insert into list */
  735.             t = a->next;
  736.             a->next = n;
  737.             n->next = t;
  738.         }
  739.     }
  740.     return (head);
  741. }
  742.  
  743. /*
  744.  *  embed_template() - Embeds the given Template t into the Template template.
  745.  *  Returns NULL on error; otherwise returns template.
  746.  */
  747. Template *embed_template(t, template)
  748. Template *t, *template;
  749. {
  750.     int nembed = 0;        /* number of embedded documents in t */
  751.     AVList *walker;
  752.     char *p, *q, buf[BUFSIZ];
  753.  
  754.     /* Find out what the last embedded document in template is */
  755.     for (walker = template->list; walker; walker = walker->next) {
  756.         if (strncasecmp(walker->data->attribute, "embed<", 6))
  757.             continue;
  758.         p = strchr(walker->data->attribute, '<') + 1;
  759.         if ((q = strchr(walker->data->attribute, '>')) != NULL)
  760.             *q = '\0';
  761.         else
  762.             continue;
  763.         nembed = (nembed < atoi(p)) ? atoi(p) : nembed;
  764.         *q = '>';    /* replace */
  765.     }
  766. #ifdef DEBUG
  767.     log("%s has %d embedded documents\n", template->url, nembed);
  768. #endif
  769.  
  770.     /* Now add all of the fields from t into template */
  771.     nembed++;
  772.     for (walker = t->list; walker; walker = walker->next) {
  773.         sprintf(buf, "Embed<%d>-%s", nembed, walker->data->attribute);
  774.         FAST_add_AVList(template->list, buf, walker->data->value,
  775.                 walker->data->vsize);
  776.         if (walker->data->offset != -1)
  777.             add_offset(template->list, buf, walker->data->offset);
  778.     }
  779.     return (template);
  780. }
  781.  
  782. /*
  783.  *  sink_embedded() - Places all of the embedded attributes at the bottom
  784.  *  of the list.  *Must* be sorted first.
  785.  */
  786. AVList *sink_embedded(list)
  787. AVList *list;
  788. {
  789.     AVList *start, *end, *walker, *last, *t;
  790.     static AVList *head;
  791.  
  792.     for (walker = last = head = list, start = end = NULL;
  793.          walker != NULL;
  794.          last = walker, walker = walker->next) {
  795.         if (!strncasecmp(walker->data->attribute, "embed", 5)) {
  796.             start = start ? start : last;
  797.         } else if (start != NULL) {
  798.             end = end ? end : last;
  799.         }
  800.     }
  801.     if (start == NULL || end == NULL) {
  802.         /* No embedded section, or at bottom of list */
  803.         return (head);
  804.     } else if (start == head) {
  805.         last->next = start;    /* Embed section at top of list */
  806.         head = end->next;
  807.         end->next = NULL;
  808.     } else {        /* Embed section at middle of list */
  809.         t = start->next;
  810.         last->next = t;
  811.         start->next = end->next;
  812.         end->next = NULL;
  813.     }
  814.     return (head);
  815. }
  816.